//Those features were calculated using the code available:
//http://www.di.ens.fr/~laptev/actions/
//Features per video are precalculated and saved in ./stip-features.
//S07-stip.txt
//You need to remode the header per each file, that looks like:
//# point-type y-norm x-norm t-norm y x t sigma2 tau2 dscr-hog(72) dscr-hof(90) 
//# walk-simple
//In the column 7(6 when starting at 0) is the label for each feature vector
//I'll use only the last 162 features as per the Zare Borzeshi's paper
//dim =162.



inline
stip_feat::stip_feat(const std::string in_path,
		     const std::string in_actionNames,  
		     const int in_dim
):path(in_path), actionNames(in_actionNames), dim(in_dim)
{
  actions.load( actionNames );  //actions.print("All actions");
  ismultiAction = false; // per default is false
}



inline
void
stip_feat::features_per_action_training( field<string>peo_train ) 
{
  std::vector < vec > feat_all_videos_action_i;
  int n_actions = actions.n_rows;
  //cout << "size " << all_actions_matrix.size() << endl;
  
  /*for (uword b=0;  b< all_actions_matrix.size(); ++b)
   *    {
   *      cout << all_actions_matrix.at(b).size() << " ";
}
*/cout << endl;


int n_peo_tr =  peo_train.n_rows;
peo_train.print("people");

for (int pe = 0; pe< n_peo_tr; ++pe)
{
  
  all_actions_matrix.clear();
  all_actions_matrix.resize(n_actions);
  
  std::stringstream ss_file_name;
  ss_file_name << "./stip-features/" <<  peo_train (pe) << "-stip-ReadytoUse.txt";
  cout << ss_file_name.str() << endl;
  
  
  std::stringstream ss_label_name;
  ss_label_name << path <<  peo_train (pe) << "_unique_verbs_labels.dat";
  arma_labels.load( ss_label_name.str(), raw_ascii ); //labels are in a frame basis.
  
  
  
  std::stringstream ss_range_frames;
  ss_range_frames << path <<  peo_train (pe) << "_start_end.dat";
  
  
  
  feature_video(ss_file_name.str(), ss_range_frames.str() ) ; //all_actions_matrix is calculated inside this method
  
  //cout << "Lo carga???? No presiones una tecla... cancela!!!" << endl;
  
  //getchar();
  
  
  cout << "size " << all_actions_matrix.size() << endl;
  
  for (uword b=0;  b< all_actions_matrix.size(); ++b)
  {
    cout << all_actions_matrix.at(b).size() << " ";
    
  }
  cout << endl;
  
  for (int act=0; act<n_actions; ++act)
  {
    feat_all_videos_action_i = all_actions_matrix.at(act);
    all_actions_matrix.at(act).clear(); // releasing memory
    //cout << feat_all_videos_action_i.size() << endl;
    //cout << feat_all_videos_action_i.at(0).size() << endl;
    
    
    mat mat_features;
    if (feat_all_videos_action_i.size()>0)
    {
      mat_features.zeros(dim,feat_all_videos_action_i.size());
      //cout << mat_features.n_rows << " " << mat_features.n_cols << endl;
      
      for (uword i = 0; i < feat_all_videos_action_i.size(); ++i)
      {
	mat_features.col(i) = feat_all_videos_action_i.at(i)/norm(feat_all_videos_action_i.at(i),2);
	//cout << i << " " ;
	
      }
    }
    else
    {
      mat_features.zeros(dim,0);
    }
    std::stringstream tmp_ss4;
    tmp_ss4 << "./features_training/feature_vectors_dim" << dim << "_" << peo_train (pe) << "_" << actions(act)<< ".dat";
    
    mat_features.save( tmp_ss4.str(), raw_ascii );
  }
}
}

// //****************** Feature Extraction**************************************
// //***************************************************************************
inline 
void
stip_feat::feature_video(std::string feat_file_name, std::string sRange_frames)
{
  //cout << "Entra aqui" << endl;
  cout << sRange_frames << endl;;
  std::vector < vec > feat_frame;
  rowvec range_frame;
  range_frame.load( sRange_frames );
  //range_frame.print("ini end");
  int ini_frame = range_frame(0);
  int end_frame = range_frame(1) - 1;
  
  
  int n_frames = arma_labels.n_elem;
  
  mat ori_features;
  ori_features.load(feat_file_name);
  cout << "ori_features rows&cols " << ori_features.n_rows << " & " << ori_features.n_cols << endl;
  vec idx_frames; //I thinks this starts at 0, as the implementation is done in OpenCV
  idx_frames = ori_features.col(6); //Column 6 corresponds to the frame index per each vector
  //idx_frames.t().print("idx_frames");
  ori_features.cols( 0, 8).zeros();
  ori_features.save("zeros_ori_data.dat",raw_ascii);
  int pos;
  //cout << "press a key" << endl;
  //getchar();
  
  //cout << "ori_features.n_rows " << ori_features.n_rows << endl;
  for(int nv=0; nv<ori_features.n_rows ; nv++){
    
    
    pos = idx_frames(nv);
    
    if ( (pos>=ini_frame-1) && (pos<=end_frame-2) )
    {
      
      //cout << "size lab " << arma_labels.n_elem << endl;
      //cout << pos - ini_frame + 1 << " " << endl;
      int lab = arma_labels(pos - ini_frame + 1);
      //cout << "lab " << lab << endl;
      rowvec feat_vec = ori_features( nv, span(9,170) );
	
      if (!ismultiAction){//To obtaing Training Features
	
	all_actions_matrix.at(lab).push_back(feat_vec.t());
      }
      
      else //To obtaing Testing Features
      {
	
	multi_features.push_back(feat_vec.t());
	frame_index.push_back(pos - ini_frame + 1);
	//lab_feature_vectors.push_back(lab);
      }
 
    }
   }
  
}

// //******************TESTING MULTI ACTIONS***********************************
// //***************************************************************************

inline 
void 
stip_feat::feature_multi_action( field<string> peo_test )
{
  ismultiAction = true; // per default is false
  
  int n_peo_test =  peo_test.n_rows;
  
  for (int pe = 0; pe <n_peo_test; ++pe ){ //videos.n_rows
    
    
    multi_features.clear(); 
    //lab_feature_vectors.clear();
    frame_index.clear();
    
    
    std::stringstream ss_file_name;
    ss_file_name << "./stip-features/" <<  peo_test (pe) << "-stip-ReadytoUse.txt";
    cout << ss_file_name.str() << endl;
    
    std::stringstream ss_label_name;
    ss_label_name << path <<  peo_test (pe) << "_unique_verbs_labels.dat";
    arma_labels.load( ss_label_name.str(), raw_ascii ); //labels are in a frame basis.
    
    
    std::stringstream ss_range_frames;
    ss_range_frames << path <<  peo_test (pe) << "_start_end.dat";
    
    
    cout << ss_file_name.str() << endl;
    feature_video(ss_file_name.str(), ss_range_frames.str() ) ;  
    
    //cout << "Total feature vectors: " << multi_features.size()<< endl;
    //cout << "Total labels: " << lab_feature_vectors.size() << endl;
    
    cout << "Converting to Arma:" << endl;
    //cout << "# of Frames: "  << lab_feature_vectors.size() << endl;
    cout << "# of Vectors: " << multi_features.size() << endl;
    
    //uvec lab_feature_vectors_arma( lab_feature_vectors.size() );
    uvec frame_index_arma( frame_index.size() );
    mat  feature_vectors_arma( dim, multi_features.size() );
    
    
    
    for (uword i = 0; i < multi_features.size(); ++i)
    {
      //cout << i << endl;
      frame_index_arma(i) = frame_index.at(i);
      feature_vectors_arma.col(i)     = multi_features.at(i)/norm(multi_features.at(i),2);
      
      //mat_features.col(i) = feat_all_videos_action_i.at(i)/norm(feat_all_videos_action_i.at(i),2);
      
    }
    
//     for (uword i = 0; i < lab_feature_vectors.size(); ++i)
//     {
//       //cout << i << endl;
//       lab_feature_vectors_arma(i) = lab_feature_vectors.at(i);
//       
//       
//     }
    
    
    std::stringstream tmp_ss4;
    tmp_ss4     << "./multi_features/feat_"<<  peo_test(pe) << ".dat";  
    
    std::stringstream tmp_vec_lab;
    tmp_vec_lab << "./multi_features/lab_" <<  peo_test(pe) << ".dat";  
    
    std::stringstream tmp_vec_fr_index;
    tmp_vec_fr_index << "./multi_features/fr_idx_" <<  peo_test(pe) <<  ".dat";  
    
    
    //cout << tmp_ss4.str() << endl;
    //cout << "Press a key " << endl;
    //getchar();
    cout << "Saving " << endl;
    feature_vectors_arma.save( tmp_ss4.str(), raw_ascii );
    //lab_feature_vectors_arma.save( tmp_vec_lab.str(), raw_ascii );
    arma_labels.save( tmp_vec_lab.str(), raw_ascii );
    frame_index_arma.save( tmp_vec_fr_index.str(), raw_ascii );
    //cout << "Press a key" << endl;
    //getchar();
    
    
  }
  
}

// //******************Universal GMM *******************************************
// //***************************************************************************

inline
void
stip_feat::create_universal_gmm(field <string>  peo_train, int N_cent, int run, bool is_pca)
{ 
  mat means;
  mat covs;
  vec weights;	 
  
  
  cout << "Calculating Universal GMM " << endl;
  cout << "# clusters: " << N_cent << endl;
  
  mat uni_features;
  
  for (uword pe=0; pe<peo_train.n_rows; ++pe){
    
    
    mat mat_features;
    
    for (uword act = 0 ; act < actions.n_rows;  ++act) {
      
      
      mat mat_features_video_i;
      std::stringstream ssName_feat_video;
      
      ssName_feat_video << "./features_training/feature_vectors_dim" << dim << "_" << peo_train(pe) << "_" << actions(act) << ".dat";
      //cout << ssName_feat_video.str() << endl;
      mat_features_video_i.load( ssName_feat_video.str() );
      
      //Using all feature vectors as STIP features are not in a  pixel basis
      if ( mat_features_video_i.n_cols>0 )
      {
	mat_features	 = join_rows( mat_features, mat_features_video_i );
	
      }
      else 
      {
	cout << "# vectors = 0 in " << ssName_feat_video.str() << endl;
	
      }
      
    }
    
    //cout << "mat_features.n_cols "<< mat_features.n_cols << endl;
    
    
    if ( mat_features.n_cols>0 )
    {
      uni_features	 = join_rows( uni_features, mat_features );
      
    }
    else 
    {
      cout << "# vectors = 0 in uni_features" << endl;
      
    }
    
    //uni_features =  join_rows( uni_features, mat_features );
    mat_features.reset();
    
    
  }
  
  
  cout << "r&c "<<  uni_features.n_rows << " & " << uni_features.n_cols << endl;
  int dim_data = uni_features.n_rows;
  int n_vec_data = uni_features.n_cols;
  int NP = floor(dim_data/4); 
  cout << "NP " << NP << endl;
  //PCA:
  
  if (is_pca)
  {
    mat U;
    vec s;
    mat V;
    mat Sigma = cov( uni_features.t() );
    svd(U,s,V,Sigma);
 
    mat trans_matrix_pca = U.cols( 0, NP-1 ); //Transformation Matrix
    
    mat tpr = uni_features.t()*trans_matrix_pca;
    
    uni_features = tpr.t();
    
    //cout << "U r&c "<<  U.n_rows << " & " << U.n_cols << endl;
    
    //cout << "Sigma r&c "<<  Sigma.n_rows << " & " << Sigma.n_cols << endl;
    
    //cout << "trans_matrix_pca r&c "<<  trans_matrix_pca.n_rows << " & " << trans_matrix_pca.n_cols << endl;
    //cout << "tpr r&c "<<  tpr.n_rows << " & " << tpr.n_cols << endl;
    //cout << "new uni_features r&c "<<  uni_features.n_rows << " & " << uni_features.n_cols << endl;
    
    std::stringstream tmp_pca;
    tmp_pca << "./run"<< run <<"/universal_GMM/pca_transf_matrix_dim" << NP <<".dat";
    cout << "Saving trans_matrix_pca in " << tmp_pca.str() << endl;
    trans_matrix_pca.save( tmp_pca.str(), raw_ascii );
    //save 
  }
  
  
  
  bool is_finite = uni_features.is_finite();
  
  if (!is_finite )
  {
    cout << "is_finite?? " << is_finite << endl;
    cout << uni_features.n_rows << " " << uni_features.n_cols << endl;
    getchar();
    
  }
  
  
  cout << "universal GMM" << endl;
  gmm_diag gmm_model;
  gmm_diag bg_model;
  
  bool status_em = false;
  int rep_em=0;
  
  int km_iter = 20;
  int em_iter  = 20;
  double var_floor = 1e-10;
  bool print_mode = true;
  
  
  
  
  while (!status_em)
  {
    bool status_kmeans = false;
    //int rep_km = 0;
    
    while (!status_kmeans)
    {
      arma_rng::set_seed_random();
      
      status_kmeans = gmm_model.learn(uni_features, N_cent, eucl_dist, random_subset, km_iter, 0, var_floor, print_mode);   //Only Kmeans
      bg_model = gmm_model;
      //rep_km++;
    }
    
    
    status_em = gmm_model.learn(uni_features, N_cent, eucl_dist, keep_existing, 0, em_iter, var_floor, print_mode);   
    rep_em++;
    
    if (rep_em==9)
    {
      status_em = true;
      gmm_model = bg_model;
      
    }
    
  }
  
  
  cout <<"EM was repeated " << rep_em << endl;
  
  
  if (is_pca)
  {
    
    //   tmp_pca << "./run"<< run <<"/universal_GMM/pca_transf_matrix_dim" << NP <<".dat";
    

    std::stringstream tmp_ss5;
    tmp_ss5 << "./run"<< run <<"/universal_GMM/pca_UniversalGMM_Ng" << N_cent << "dim" << NP <<".dat";
    cout << "Saving GMM in " << tmp_ss5.str() << endl;
    gmm_model.save( tmp_ss5.str() );
    cout << endl;
    
    
    means = gmm_model.means;
    covs  = gmm_model.dcovs;
    weights = gmm_model.hefts.t();	
    
    //Saving statistics
    std::stringstream ss_weigths;
    ss_weigths << "./run"<< run <<"/universal_GMM/pca_weights_Ng" << N_cent << "dim" << NP <<".dat"; 
    
    std::stringstream ss_means;
    ss_means << "./run"<< run <<"/universal_GMM/pca_means_Ng" << N_cent << "dim" << NP <<".dat"; 
    
    std::stringstream ss_covs;
    ss_covs << "./run"<< run <<"/universal_GMM/pca_covs_Ng" << N_cent << "dim" << NP <<".dat"; 
    
    weights.save( ss_weigths.str(), raw_ascii );
    means.save( ss_means.str(), raw_ascii );
    covs.save(ss_covs.str(), raw_ascii);
  }
  
  else
  {
    
    std::stringstream tmp_ss5;
    tmp_ss5 << "./run"<< run <<"/universal_GMM/UniversalGMM_Ng" << N_cent << "dim" << NP <<".dat";
    cout << "Saving GMM in " << tmp_ss5.str() << endl;
    gmm_model.save( tmp_ss5.str() );
    cout << endl;
    means = gmm_model.means;
    covs  = gmm_model.dcovs;
    weights = gmm_model.hefts.t();	
    
    //Saving statistics
    std::stringstream ss_weigths;
    ss_weigths << "./run"<< run <<"/universal_GMM/weights_Ng" << N_cent<< "dim" << NP <<".dat"; 
    
    std::stringstream ss_means;
    ss_means << "./run"<< run <<"/universal_GMM/means_Ng" << N_cent<< "dim" << NP <<".dat"; 
    
    std::stringstream ss_covs;
    ss_covs << "./run"<< run <<"/universal_GMM/covs_Ng" << N_cent<< "dim" << NP <<".dat"; 
    
    weights.save( ss_weigths.str(), raw_ascii );
    means.save( ss_means.str(), raw_ascii );
    covs.save(ss_covs.str(), raw_ascii);}
}


/*
 * 
 * 
 * 
 * 
 i n*line
 void
 stip_feat::create_universal_gmm(field <string>  peo_train, int N_cent, int run)
 { 
 mat means;
 mat covs;
 vec weights;	
 
 
 cout << "Calculating Universal GMM " << endl;
 cout << "# clusters: " << N_cent << endl;
 
 mat uni_features;
 
 for (uword pe=0; pe<peo_train.n_rows; ++pe)    {
   
   cout << "Doing for person: " << peo_train (pe) << endl;
   mat mat_features_tmp;
   mat mat_features;
   
   for (uword act = 0 ; act < actions.n_rows;  ++act) {
     
     
     mat mat_features_video_i;
     std::stringstream ssName_feat_video;
     ssName_feat_video << "./features_training/feature_vectors_dim" << dim << "_" << peo_train (pe) << "_" << actions(act)<< ".dat";
     //cout << ssName_feat_video.str() << endl;
     mat_features_video_i.load( ssName_feat_video.str() );
     
     if ( mat_features_video_i.n_cols>0 )
     {
     mat_features_tmp	 = join_rows( mat_features_tmp, mat_features_video_i );
     
     }
     else 
     {
     cout << "# vectors = 0 in " << ssName_feat_video.str() << endl;
     
     }
     
     mat_features_video_i.reset();
     
     
     }
     
     cout << "mat_features_tmp.n_cols "<< mat_features_tmp.n_cols << endl;
     const uword N_max = 100000;  
     
     if (mat_features_tmp.n_cols > N_max)
     {
     ivec tmp1 = randi( N_max, distr_param(0,mat_features_tmp.n_cols-1) );
     ivec tmp2 = unique(tmp1);
     uvec my_indices = conv_to<uvec>::from(tmp2);
     mat_features = mat_features_tmp.cols(my_indices);  // extract a subset of the columns
     
     }
     else
     {
     mat_features = mat_features_tmp;
     
     }
     
     cout << "mat_features.n_cols "<< mat_features.n_cols << endl;
     
     if ( mat_features.n_cols>0 )
     {
     uni_features	 = join_rows( uni_features, mat_features );
     
     }
     else 
     {
     cout << "# vectors = 0 in uni_features" << endl;
     
     }
     
     //uni_features =  join_rows( uni_features, mat_features );
     mat_features_tmp.reset();
     mat_features.reset();
     
     
     }
     
     
     cout << "r&c "<<  uni_features.n_rows << " & " << uni_features.n_cols << endl;
     bool is_finite = uni_features.is_finite();
     
     if (!is_finite )
     {
     
     cout << "is_finite?? " << is_finite << endl;
     cout << uni_features.n_rows << " " << uni_features.n_cols << endl;
     getchar();
     
     }
     
     
     cout << "universal GMM" << endl;
     gmm_diag gmm_model;
     gmm_diag bg_model;
     
     bool status_em = false;
     int rep_em=0;
     
     int km_iter = 20;
     int em_iter  = 20;
     double var_floor = 1e-10;
     bool print_mode = true;
     
     
     
     
     while (!status_em)
     {
     bool status_kmeans = false;
     //int rep_km = 0;
     
     while (!status_kmeans)
     {
     arma_rng::set_seed_random();
     
     status_kmeans = gmm_model.learn(uni_features, N_cent, eucl_dist, random_subset, km_iter, 0, var_floor, print_mode);   //Only Kmeans
     bg_model = gmm_model;
     //rep_km++;
     }
     
     
     status_em = gmm_model.learn(uni_features, N_cent, eucl_dist, keep_existing, 0, em_iter, var_floor, print_mode);   
     rep_em++;
     
     if (rep_em==9)
     {
     status_em = true;
     gmm_model = bg_model;
     
     }
     
     }
     
     
     cout <<"EM was repeated " << rep_em << endl;
     
     std::stringstream tmp_ss5;
     tmp_ss5 << "./run"<< run <<"/universal_GMM/UniversalGMM_Ng" << N_cent << "_dim" <<dim;
     cout << "Saving GMM in " << tmp_ss5.str() << endl;
     gmm_model.save( tmp_ss5.str() );
     cout << endl;
     
     
     means = gmm_model.means;
     covs  = gmm_model.dcovs;
     weights = gmm_model.hefts.t();	
     
     //Saving statistics
     std::stringstream ss_weigths;
     ss_weigths << "./run"<< run <<"/universal_GMM/weights_Ng" << N_cent << "_dim" << dim; 
     
     std::stringstream ss_means;
     ss_means << "./run"<< run <<"/universal_GMM/means_Ng" << N_cent << "_dim" << dim; 
     
     std::stringstream ss_covs;
     ss_covs << "./run"<< run <<"/universal_GMM/covs_Ng" << N_cent << "_dim" << dim; 
     
     weights.save( ss_weigths.str(), raw_ascii );
     means.save( ss_means.str(), raw_ascii );
     covs.save(ss_covs.str(), raw_ascii);
     
     }
     */